//! 高阶函数 高阶函数与普通函数的不同在于，它可以使用一个或多个函数作为参数，可以将函数作为返回值。
//! rust的函数是first class type，所以支持高阶函数。而，由于rust是一个强类型的语言，
//! 如果要将函数作为参数或返回值，首先需要搞明白函数的类型。下面先说函数的类型，再说函数作为参数和返回值。

///函数类型:关键字fn可以用来定义函数。除此以外，它还用来构造函数类型。与函数定义主要的不同是，
/// 构造函数类型不需要函数名、参数名和函数体
fn inc(n: i32) -> i32 {//函数定义
    n + 1
}

//函数类型
type IncType = fn(i32) -> i32;

#[test]
pub fn high_class_fun_demo1(){
    let fun: IncType = inc;
    println!("{}", fun(3));
    println!("{}", inc(4));//还是可以使用，inc函数的所有权并没有被转移到func变量上，而是更像不可变引用。
    // 在rust中，函数的所有权是不能转移的，我们给函数类型的变量赋值时，赋给的一般是函数的指针，
    // 所以rust中的函数类型，就像是C/C++中的函数指针，当然，rust的函数类型更安全。可见，
    // rust的函数类型，其实应该是属于指针类型（Pointer Type）。rust的Pointer Type有两种，一种为引用（Reference&），
    // 另一种为原始指针（Raw pointer *），详细内容请看Rust Reference 8.18 Pointer Types。
    // 而rust的函数类型应是引用类型，因为它是安全的，而原始指针则是不安全的，要使用原始指针，必须使用unsafe关键字声明。
}

///函数作为参数
//process就是一个高阶函数，它有两个参数，一个类型为i32的n，另一个类型为fn(i32)->i32的函数func，
// 返回一个i32类型的参数；它在函数体内以n作为参数调用func函数，返回func函数的返回值
fn process(n: i32, func: fn(i32) -> i32) -> i32 {
    func(n)
}

//不过，这不是函数作为参数的唯一声明方法，使用泛型函数配合特质（trait）也是可以的，因为rust的函数都会实现一个
// trait:FnOnce、Fn或FnMut。将上例中的process函数定义换成以下形式是等价的：

fn process2<F>(n: i32, func: F) -> i32 where F: Fn(i32) -> i32 {
    func(n)
}

#[test]
pub fn high_class_fun_demo2(){
    println!("{}", process(23, inc));
    println!("{}", process2(23, inc));
}

///函数作为返回值
///高阶函数为get_func，它接收一个i32类型的参数，返回一个类型为fn(i32) -> i32的函数，若传入的参数为偶数，
/// 返回inc，否则返回dec。这里需要注意的是，inc函数和dec函数都定义在get_func内。在函数内定义函数在很多其他语言中
/// 是不支持的，不过rust支持，这也是rust灵活和强大的一个体现。
/// 不过，在函数中定义的函数，不能包含函数中（环境中）的变量，若要包含，应该闭包（
fn get_func(n: i32) -> fn(i32) -> i32 {
    //定义函数
    fn inc(n: i32) -> i32 {
        n + 1
    }
    fn dec(n: i32) -> i32 {
        n - 1
    }
    if n % 2 == 0 {
        inc
    } else {
        dec
    }
}

#[test]
pub fn high_class_fun_demo3(){
   let f = get_func(23);
    println!("{}", f(45));
}